﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

using Autofac;
using Autofac.Extensions.DependencyInjection;

using Jack.EntityFrameworkCore.ConsoleTest.Entities;

using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;

namespace Jack.EntityFrameworkCore.ConsoleTest
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            IServiceCollection services = new ServiceCollection();
            services.AddJackDbContext<Entities.DemoDbContext>(options =>
            {
                string connStr = "server=127.0.0.1;Database=demo_db;Uid=root;Pwd=change_me;Port=3306;Allow User Variables=True;SslMode=None;CharSet=UTF8;";
                options.UseMySql(connStr, ServerVersion.AutoDetect(connStr), mySqlOptions =>
                {
                    mySqlOptions.CommandTimeout(3);
                    mySqlOptions.MaxBatchSize(50);
                });
            }, new JackDbContextOptions { AddDefaultLogger = true, AddDefaultRepository = true });

            var builder = new ContainerBuilder();
            builder.Populate(services);
            builder.RegisterType<TestContext>().InstancePerLifetimeScope().PropertiesAutowired();

            IContainer container = builder.Build();
            IServiceProvider serviceProvider = new AutofacServiceProvider(container);

            using (var scope = serviceProvider.CreateScope())
            {
                TestContext testContext = scope.ServiceProvider.GetService<TestContext>();
                TestContext testContext1 = serviceProvider.GetService<TestContext>();

                DemoDbContext demoDbContext = scope.ServiceProvider.GetRequiredService<DemoDbContext>();
                //demoDbContext.Database.GetConnectionString()
                //demoDbContext.Database.GetDbConnection()
                //demoDbContext.Database.IsMySql()
                //demoDbContext.Database.SetConnectionString("")
                //demoDbContext.Database.SetDbConnection()
                //demoDbContext.Database.UseTransaction()
                //var sqlScript = demoDbContext.Set<UserModel>("my_user_table").ToSql();

                demoDbContext.SetTenantId("demo_tenantid");
                var sqlScript = demoDbContext.Set<UserModel>().ToSql();
                Console.WriteLine(sqlScript);
                demoDbContext.TestChangeTable<UserModel>();

                IEfCoreUnitOfWork<DemoDbContext> efCoreUnitOfWork = scope.ServiceProvider.GetService<IEfCoreUnitOfWork<DemoDbContext>>();
                TestContext testContext2 = efCoreUnitOfWork.DbContext.GetService<TestContext>();

                IEfCoreUnitOfWork<DemoDbContext> efCoreUnitOfWork1 = serviceProvider.GetService<IEfCoreUnitOfWork<DemoDbContext>>();
                TestContext testContext3 = efCoreUnitOfWork1.DbContext.GetService<TestContext>();

                Console.WriteLine(testContext == testContext1);
                Console.WriteLine(Object.ReferenceEquals(testContext, testContext1));

                Console.WriteLine(testContext.UserRepository == testContext1.UserRepository);
                Console.WriteLine(Object.ReferenceEquals(testContext.UserRepository, testContext1.UserRepository));

                Console.WriteLine(testContext == testContext2);
                Console.WriteLine(Object.ReferenceEquals(testContext, testContext2));

                Console.WriteLine(testContext3 == testContext2);
                Console.WriteLine(Object.ReferenceEquals(testContext3, testContext2));

                Console.WriteLine(testContext3 == testContext1);
                Console.WriteLine(Object.ReferenceEquals(testContext3, testContext1));

                var list = await testContext3.UserRepository.Take(10).ToDynamicListAsync();
                Console.WriteLine(list.Count);

                //string strFilter = "x => x.Age > 0";
                //var options = ScriptOptions.Default.AddReferences(typeof(UserModel).Assembly);
                //Func<UserModel, bool> predicate = await CSharpScript.EvaluateAsync<Func<UserModel, bool>>(strFilter, options);

                //var parameter = Expression.Parameter(typeof(UserModel), "x");
                //var comparison = Expression.GreaterThan(Expression.Property(parameter, typeof(UserModel).GetProperty("Age")), Expression.Constant(0));
                //var lambda = Expression.Lambda<Func<UserModel, bool>>(comparison, parameter);

                //var xxx = await testContext3.UserRepository.Where(lambda).ToListAsync();

                //xxx = xxx.Where(predicate).ToList();

                //var xxx = await testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName").ToListAsync();

                var sql = testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName DESC").ToSql();
                Console.WriteLine(sql);

                var sql2 = testContext3.UserRepository.Where(x => x.Age > 0).OrderBy(x => x.Age).ThenByDescending(x => x.UserName).ToSql();
                Console.WriteLine(sql2);

                var data = testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName DESC").Select<UserRecord>("new(UserName,Age)").ToList();
                var sql3 = testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName DESC").Select<UserRecord>("new(UserName,Age)").ToSql();
                Console.WriteLine(sql3);

                var data2 = testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName DESC").SelectFields("UserName", "Age").ToList();
                var sql4 = testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName DESC").SelectFields("UserName", "Age").ToSql();
                Console.WriteLine(sql4);

                var data3 = await testContext3.UserRepository.Where("Age > 0").OrderBy("Age").ThenBy("UserName DESC").Select("new{UserName,Age}").ToDynamicListAsync();
                Console.WriteLine(JsonSerializer.Serialize(data3));
            }
        }

        public record UserRecord(string UserName, int Age);
    }
}

// https://github.com/filipw/csharp-string-to-lambda-example/blob/master/ConsoleApp6/Program.cs
// https://dynamic-linq.net/
// https://github.com/zzzprojects/System.Linq.Dynamic.Core
// https://www.stevefenton.co.uk/2015/07/getting-the-sql-query-from-an-entity-framework-iqueryable/
// System.Linq.Dynamic动态查询 https://blog.csdn.net/csdmwinter/article/details/89969378
// https://code-maze.com/using-dynamic-linq/
// https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations
